using System.Collections.Generic;
using SymbolIntegration.core.Helpers;

namespace SymbolIntegration.core.Expressions
{
    public class DivideExpression : Expression
    {
        private readonly Expression m_Numerator;
        private readonly Expression m_Denominator;

        internal static DivideExpression ParseItself(string expression, int position, int size)
        {
            int oldPosition = position;
            int oldSize = size;
            DivideExpression result = null;
            ParseUtils.TrimScopes(expression, ref position, ref size);
            if (size == 0)
            {
                throw new ParseException("Expression is scopes or spaces. Need power", expression, oldPosition, oldSize,
                    0);
            }
            int endPosition = position + size - 1;

            for (int i = position; i <= endPosition; i++)
            {
                if (expression[i] == '(')
                {
                    i = ParseUtils.FindCloseScopePosition(expression, i, size + position - i);
                }
                if (expression[i] == '/')
                {
                    var numerator = ParseUtils.Parse(expression, position, i - position);
                    var denominator = ParseUtils.Parse(expression, i + 1, endPosition - i);
                    result = new DivideExpression(numerator, denominator);
                    break;
                }
            }

            return result;
        }

        public DivideExpression(Expression numerator, Expression denominator)
        {
            m_Numerator = numerator;
            m_Denominator = denominator;
        }

        public override ExpressionType Type
        {
            get { return ExpressionType.Divide;}
        }

        public override Expression Clone()
        {
            var result = new DivideExpression(m_Numerator, m_Denominator);
            InnerCopy(result);
            return result;
        }

        public override Expression Substitute(Dictionary<string, Expression> varValues)
        {
            var result = new DivideExpression(m_Numerator.Substitute(varValues),
                m_Denominator.Substitute(varValues));
            InnerCopy(result);
            return result;
        }

        public override Expression Differentiate(string variable)
        {
            Expression result;
            do
            {
                if (!IsDependsFrom(variable))
                {
                    result = new ConstantExpression(0);
                    break;
                }

                result = (m_Numerator.Differentiate(variable) * m_Denominator 
                    - m_Numerator  *m_Denominator.Differentiate(variable)) / (m_Denominator * m_Denominator);
                result = result.InvertNegate(IsNegate);
            } while (false);

            return result;
        }

        public override Expression Normalize()
        {
            Expression result;
            do
            {
                var normalDenominator = m_Denominator.Normalize();
                var normalNumerator = m_Numerator.Normalize();
                var constantDenominator = normalDenominator as ConstantExpression;
                var constantNumerator = normalNumerator as ConstantExpression;
                //Check in case of constants
                if (constantDenominator != null)
                {
                    if (constantNumerator != null)
                    {
                        double value = constantNumerator.Value/constantDenominator.Value;
                        value = IsNegate ? -value : value;
                        result = new ConstantExpression(value);
                        break;
                    }

                    if (MathHelper.AlmostEqual(constantDenominator.Value, 1))
                    {
                        result = normalNumerator.InvertNegate(IsNegate).Normalize();
                        break;
                    }
                }

                result = normalNumerator*new PowerExpression(normalDenominator, new ConstantExpression(-1));
                InnerCopy(result);
                result = result.Normalize();
            } 
            while (false);

            return result;
        }

        public override bool IsEqual(Expression right)
        {
            bool result = false;
            do
            {
                if (right.IsNegate != IsNegate)
                {
                    break;
                }

                var div = right as DivideExpression;
                if (div == null)
                {
                    break;
                }

                if (!div.m_Numerator.IsEqual(m_Numerator))
                {
                    break;
                }

                if (!div.m_Denominator.IsEqual(m_Denominator))
                {
                    break;
                }

                result = true;
            }
            while (false);

            return result;
        }

        public override Polinom FormatToPolinomAsDependentFromVariable(string variable)
        {
            return null;
        }

        public override string ToString()
        {
            var result = "";
            if (m_Numerator.Type == ExpressionType.Addends)
            {
                result += "(" + m_Numerator + ")";
            }
            else
            {
                result += m_Numerator;
            }
            result += "/";
            if (m_Numerator.Type == ExpressionType.Addends)
            {
                result += "(" + m_Denominator + ")";
            }
            else
            {
                result += m_Denominator;
            }
            if (IsNegate)
            {
                result = "-" + result;
            }

            return result;
        }

        protected internal override void RebuildDependancyCache()
        {
            m_Cache.Varibles.Clear();
            if (!m_Numerator.m_Cache.IsValid)
            {
                m_Numerator.RebuildDependancyCache();
            }
            m_Cache.Varibles.UnionWith(m_Numerator.m_Cache.Varibles);

            if (!m_Denominator.m_Cache.IsValid)
            {
                m_Denominator.RebuildDependancyCache();
            }
            m_Cache.Varibles.UnionWith(m_Denominator.m_Cache.Varibles);

            m_Cache.IsValid = true;
        }

        protected internal override void FillExpressionStatistic(ExpressionStatistic statistic, string integrateVariable)
        {
            throw new System.NotImplementedException("Class must be normalized");
        }

        protected internal override Expression IntegrateAsDependentFromVar(string variable, IIntegrator integrator)
        {
            throw new System.NotImplementedException("Class must be normalized");
        }
    }
}